package com.cnbot.dchttpsdk.api;

import com.cnbot.dchttpsdk.helper.DownFileProgressListener;
import com.cnbot.dchttpsdk.interceptor.CacheControlInterceptor;
import com.cnbot.dchttpsdk.interceptor.DownloadProgressInterceptor;
import com.google.gson.Gson;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * Copyright (c) 2016-a  Hunan Cnbot Co., Ltd. All Rights Reserved.
 *
 * @descriptoin rx请求基类
 * @FileName: ApiEngine.java
 * @author: dc
 * @date: 2019/1/19 14:08
 * @version: 1.0
 */

public final class ApiEngine<T> {
    private final String TAG = "ApiEngine";

    /**
     * 下载默认超时时间
     */
    private static final int DEFAULT_TIMEOUT = 30;
    public static final String MEDIA_TYPE = "application/json";
    public static final String MEDIA_TYPE_TEXT = "text/html";
    private OkHttpClient mOkHttpClient;
    private static ApiEngine sInstance = null;
    private Map<String, T> mApiMap = new HashMap<>();
    private static String mBaseUrl;
    private Gson mGson;
    private Class<T> mApiClazz;
    /**
     * 下载文件的进度监听
     */
    private DownFileProgressListener mDownFileListener;
    /**
     * 下载文件的路径
     */
    private String mDownFilePath;

    private ApiEngine() {
        mGson = new Gson();
        initOkHttpClient();

    }

    private ApiEngine(String baseUrl, Class<T> clazz) {
        this();
        mBaseUrl = baseUrl;
        mApiClazz = clazz;
        Retrofit retrofit = initRetrofit(mBaseUrl);
        T userApi = initApi(retrofit);
        mApiMap.put(mBaseUrl, userApi);
    }

    /**
     * 需要更改服务器根地址时调用
     * @param baseUrl
     * @return
     */
    public synchronized ApiEngine change(String baseUrl) {
        mBaseUrl = baseUrl;
        if (!mApiMap.containsKey(baseUrl)) {
            initOkHttpClient();
            Retrofit retrofit = initRetrofit(baseUrl);
            T userApi = initApi(retrofit);
            mApiMap.put(baseUrl, userApi);
        }
        return this;
    }

    /**
     *
     * @return
     */
    public static ApiEngine build() {

        if (sInstance == null) {
            throw new NullPointerException("you should init api engine in you application");
        }
        return sInstance;
    }

    public static <T> ApiEngine build(String baseUrl, Class<T> apiService) {
        if (sInstance == null) {
            synchronized (ApiEngine.class) {
                sInstance = new ApiEngine(baseUrl, apiService);
            }
        }
        return sInstance;
    }

    /**
     * 双层加锁单例,优先使用该方法初始化传入服务器根地址
     * @return
     */
    private static ApiEngine build(String baseUrl) {
        mBaseUrl = baseUrl;
        if (sInstance == null) {
            synchronized (ApiEngine.class) {
                sInstance = new ApiEngine();
            }
        }
        return sInstance;
    }

    /**
     * 初始化服务接口代理类
     * @param retrofit
     */
    private T initApi(Retrofit retrofit) {
        final T userApi = retrofit.create(mApiClazz);
        return userApi;

    }

    public T getApiService() {

        return mApiMap.get(mBaseUrl);
    }

    /**
     * 根据服务器根地址获取对应的接口
     * @param url
     * @return
     */
    public T getApiService(String url) {

        return mApiMap.get(url);
    }

    /**
     * 下载文件的接口对象
     * @param url
     * @param l
     * @return
     */
    public T getApiService(String url, DownFileProgressListener l, String path) {
        //TODO:
        mDownFileListener = l;
        mDownFilePath = path;
        initOkHttpClient3();
        Retrofit retrofit = initRetrofit2(url);
        T userApi = initApi(retrofit);

        return userApi;
    }

    /**
     * 初始化Retrofit
     * @param baseUrl
     * @return
     */
    private Retrofit initRetrofit(String baseUrl) {
        //让Retrofit和RxJava结合使用
        Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).client(mOkHttpClient).addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                //用于Gson解析
                .addConverterFactory(GsonConverterFactory.create()).build();
        return retrofit;

    }

    /**
     * 初始化Retrofit
     * 提供下载
     * @param baseUrl
     * @return
     */
    private Retrofit initRetrofit2(String baseUrl) {
        //让Retrofit和RxJava结合使用
        Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl).client(mOkHttpClient).addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        return retrofit;

    }

    /**
     * 初始化OkHttp
     */
    private void initOkHttpClient() {
        if (mOkHttpClient != null) {
            return;
        }
        initOkHttpClient2();
    }

    /**
     * 初始化OkHttp
     */
    private void initOkHttpClient2() {
        //日志拦截器
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        // 云端响应头拦截器，用来配置缓存策略
        CacheControlInterceptor cacheInterceptor = new CacheControlInterceptor();
        mOkHttpClient = new OkHttpClient.Builder()
                //                            .cache(cache)
                //注册应用拦截器
                .addInterceptor(cacheInterceptor)
                //注册网络拦截器
                .addNetworkInterceptor(cacheInterceptor).addInterceptor(logInterceptor)
                //                            .addNetworkInterceptor(new StethoInterceptor())
                .retryOnConnectionFailure(true).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).build();
    }
    /**
     * 初始化OkHttp
     * 专供下载使用
     */
    private void initOkHttpClient3() {

        //下载进度拦截器
        DownloadProgressInterceptor downloadInterceptor = new DownloadProgressInterceptor(mDownFileListener, mDownFilePath);
        mOkHttpClient = new OkHttpClient.Builder()
                //                            .cache(cache)
                //注册网络拦截器
                .addNetworkInterceptor(downloadInterceptor)
                //                            .addNetworkInterceptor(new StethoInterceptor())
                .retryOnConnectionFailure(true).connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS).build();
    }

    /**
     * 指定订阅、注销、回调线程
     * @param o
     * @param s
     * @param <T>
     */
    public <T> void toSubscribe(Observable<T> o, Subscriber<T> s) {
        // 指定subscribe发生在IO线程
        o.subscribeOn(Schedulers.io())
                //在IO线程注销
                .unsubscribeOn(Schedulers.io())
                // 指定回调发生在主线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(s);
    }

    /**
     * 默认服务器根地址为HTTP_SERVER_IP
     * @return
     */
    public T getApiProxy() {
        return (T) ApiEngine.build().getApiService();
    }

    /**
     * 针对不同的服务器根地址
     * @param baseUrl
     * @return
     */
    public T getApiProxy(String baseUrl) {
        return (T) ApiEngine.build().change(baseUrl).getApiService();
    }

    /**
     * 全局定义唯一的Gson解析器，提高初始化速度
     * @return
     */
    public Gson getParser() {
        return mGson;
    }

    static <T> T checkNotNull(T object, String message) {
        if (object == null) {
            throw new NullPointerException(message);
        }
        return object;
    }

    public static final class Builder<D> {
        private Class<D> mApiService;
        private String mBaseUrl;

        public Builder() {

        }

        /**
         * 初始化ApiEngine对象，建议在自定义application中初始化
         * @return
         */
        public <D> ApiEngine build() {
            checkNotNull(mBaseUrl, "baseUrl must not be null");
            checkNotNull(mApiService, "apiService must not be null");

            return ApiEngine.build(mBaseUrl, mApiService);

        }

        public Builder baseUrl(String baseUrl) {
            mBaseUrl = checkNotNull(baseUrl, "baseUrl must not be null");
            return this;
        }

        public Builder<D> apiService(Class<D> clazz) {
            mApiService = checkNotNull(clazz, "apiService must not be null");
            return this;
        }

    }

}
